<?php
/**
 * Klasse zum Aufbau und Validierung von Formularen
 *
 * Pflichtfelder werden immer validiert
 * Optionale Werte werden nur dann validiert, wenn der Wert nicht dem leeren String "" entspricht
 * Required-Definition wird im HTML-Code automatisch erstellt
 *
 * Bitte beachten:
 * Optionale Auswahlfehlder "<select>", welche auf ein numerisches Intervall ueberprueft werden sollen
 * muessen einen Eintrag mit value="" enthalten, da diese sonst !="" waeren und somit als zu ueberpruefende
 * optionalfelder interpretiert werden.
 *
 * Nicht betroffen sind "<select>"-Felder, welche mit CheckPartOfArray() validiert werden
 *
 * @author		Joachim Ruf
 * @copyright	Copyright (c) 2003-2013 Loresoft Software (http://www.loresoft.de)
 * @license		http://yapary.loresoft.de/lizenzbedingungen     New BSD License
 *
 * */


Class Yapary_Form_Formular_Element
{
	private $arrParam			= array();
	private $arrSelectParam		= array();
	private $arrRadioParam		= array();
	private $arrValidation		= array();

	private $error				= NULL;
	private $elementKind		= NULL; // input|select|textarea
	private $elementType		= NULL; // text|password|file|radio|checkbox|...|textarea|select|...
	private $subject			= '';
	private $method				= 'post';
	private $required			= false;
	private $enableErrorReporting	= false;
	private $isValid			= false;
	private $useSessionLookup	= false;

	/**
	 * @param string $_method	: post|get
	 * @param string $_desc		: Beschreibung
	 * */
	function __construct ($_element = '') {
		$this->setElement($_element);
	}


	public function useSessionLookup ($_boolean = true) {
		$this->useSessionLookup	= $_boolean;
	}


	/**
	 * Liefert den ELementnamen name="" zurueck
	 * @return string
	 * */
	public function getName() {
		return $this->arrParam['name'];
	}


	/**
	 * Abfrage, ob das Element als Required definiert wurde
	 * @return bool
	 * */
	public function isRequired() {
		return $this->required;
	}


	/**
	 * Hilfsklasse zur Ausgabe des Wertes eines Parameters
	 * */
	private function serializeParam($arrList, $useEndFlag= false) {
		$arrIgnore	= array('input', 'select');
		$result = '';

		if (count($arrList)) {
			foreach ($arrList as $key => $value) {
				if (!in_array($key, $arrIgnore)) {
					$result .= $key.'="'.$value.'" ';
				}
			}
		}

		return $result;
	}


	/**
	 * Hilfsklasse zur Ausgabe des Wertes eines Parameters
	 * */
	private function unserializeParam($string) {
		$arrParam = array();
		preg_match_all('/\s*([a-z]+)\s*(=\s*\"([^\"]*)\")?/ims', $string, $arrList);

		if (count($arrList)) {
			foreach ($arrList[1] as $entry => $name) {
				$value = $arrList[3][$entry];
				$arrParam[$name] = $value;
			}
		}

		return $arrParam;
	}

	private function serializeSelectParam($arrList) {
		$result = '';

		if (count($arrList)) {
			foreach ($arrList as $key => $list) {
				$arr = $list['arr'];
				$result .= '<option '.$this->serializeParam($arr).'>'.$list['name'].'</option>';
			}
		}

		return $result;
	}

	private function unserializeSelectParam($string) {
		preg_match_all('/\<\s*option([^\>]*)\>([^\<]{0,})\<\/option\>/ims', $string, $arrList);

		if (count($arrList)) {
			foreach ($arrList[1] as $entry => $name) {
				$value = $arrList[2][$entry];

				$arrTmp = $this->unserializeParam($name);
				$id = $arrTmp['value'];
				$arrParam[$id] = array('name' => $value, 'arr' => $arrTmp);
			}
		}

		return $arrParam;
	}


	/**
	 * Hilfsklasse zur Ausgabe des Wertes eines Select-Elements
	 * */
	private function unserializeRadioParam($string) {
		$arrParam = array();
		preg_match_all('/\<\s*input([^>]*)\>/ims', $string, $arrTmp);

		if (count($arrTmp)) {
			foreach ($arrTmp[1] as $key => $val) {
				$arr = $this->unserializeParam($val);
				if (!isset($arr['name'])) {
					continue;
				}
				if (!isset($arr['value'])) {
					continue;
				}

				$value = $arr['value'];
				$arrParam[$value] = $this->unserializeParam($val);
			}
		}

		return $arrParam;
	}


	/**
	 * Setzt die Beschreibung.
	 * @param string $_desc		: Beschreibung
	 * @return void
	 * */
	public function getElementKind () {
		return $this->elementKind;
	}


	/**
	 * Setzt die Beschreibung.
	 * @param string $_desc		: Beschreibung
	 * @return void
	 * */
	private function setElement ($_element) {
		preg_match('/\<\s*(\w*)[^\>]*name=\"\w*\"[^\>]*\>/ims', $_element, $arrElement);
		if (!isset($arrElement[0])) {
			return false;
		}

		$this->arrParam = $this->unserializeParam($arrElement[0]);
		$this->elementKind = $arrElement[1];

		switch ($this->elementKind) {
			case 'input':
				if (!isset($this->arrParam['type'])) {
					return false;
				}

				$this->elementType = $this->arrParam['type'];
				switch ($this->arrParam['type']) {
					case 'checkbox':
					case 'radio':
						$this->arrRadioParam = $this->unserializeRadioParam($_element);
						break;
					default: // text, password, email etc.
				}
				$this->subject = preg_replace('/\<input([^\>]*)\>/ims', '{{REPLACE_ID}}', $_element);
				break;
			case 'textarea':
				$this->subject = preg_replace('/\<textarea(.*?)\/textarea\>/ims', '{{REPLACE_ID}}', $_element);
				$this->elementType = 'textarea';
				break;
			case 'button':
				$this->subject = $_element;
				$this->elementType = 'button';
				break;
			case 'select':
				$this->subject = preg_replace('/\<select(.*?)\/select\>/ims', '{{REPLACE_ID}}', $_element);
				$this->arrSelectParam = $this->unserializeSelectParam($_element);
				$this->elementType = 'select';
				break;
		}
	}


	/**
	 * Liefert eine Zeile zurueck.
	 * @param int $i	: Auszugebende Zeile
	 * @return Yapary_Parameter_ICheck
	 * */
	public function addValidation (Yapary_Parameter_ICheck $_validation, $_required = false) {
		$this->required = $_required;
		return $this->arrValidation[] = $_validation;
	}


	/**
	 * @desc Fuegt eine extern validierte Fehlermeldung hinzu
	 * @param string $_error	: Fehlermeldung
	 * @return void
	 * */
	public function setExternalError ($_error) {
		$this->error = $_error;
	}


	/**
	 * Liefert eine Zeile zurueck.
	 * @param int $i	: Auszugebende Zeile
	 * @return Yapary_Parameter_ICheck
	 * */
	public function getValidation ($i) {
		if (!isset($this->arrValidation[$i])) {
			return NULL;
		}

		return $this->arrValidation[$i];
	}


	/**
	 * @desc Liefert alle Zeilen zurueck.
	 * @return Yapary_Parameter_ICheck[]
	 * */
	public function getValidationList () {
		return $this->arrValidation;
	}


	/**
	 * Loescht einen Eintrag.
	 * @param string $id	: Zu loeschender Eintrag
	 * @return bool
	 * */
	public function deleteValidation ($i) {
		if (!isset($this->arrValidation[$i])) {
			return false;
		}

		unset($this->arrValidation[$i]);
		return true;
	}


	/**
	 * Setzt manuell einen Wert
	 * */
	public function setValue ($_value) {
		/**
		 * @TODO Funktionoert derzeit nur fuer Input-Elemente
		 * */
		switch ($this->elementKind) {
			case 'input':
			case 'textarea':
				$this->arrParam['value'] = $_value;
				break;
			default:
		}
	}


	/**
	 * @desc Gibt an, ob das Formular bereits versendet wurde.
	 * @return mixed
	 * */
	public function getValue () {
		$key = $this->getName();
		if ($this->elementType == 'file') {
			if (isset($_FILES[$key]['type'])) {
				return $_FILES[$key];
			}
		} else {
			switch ($this->method) {
				case 'post':
					if (isset($_POST[$key])) {
						return $_POST[$key];
					} else if ($this->useSessionLookup === true) {
						if (isset($_SESSION[$key])) {
							return $_SESSION[$key];
						}
					}
					break;
				case 'get':
					if (isset($_GET[$key])) {
						return $_GET[$key];
					} else if ($this->useSessionLookup === true) {
						if (isset($_SESSION[$key])) {
							return $_SESSION[$key];
						}
					}
					break;
			}
		}

		return NULL;
	}


	/**
	 * @desc Gibt an, ob das Formular bereits versendet wurde.
	 * @return bool
	 * */
	public function hasSend () {
// 		switch ($this->elementType) {
//			case 'checkbox':
//			case 'radio':
// 				if (gettype($this->getValue($this->arrParam['name'].'_form_formular_element')) != 'NULL'){ // @TODO minimieren
// 					return true;
// 				}
// 				break;
// 			default:
// 				if (gettype($this->getValue($this->arrParam['name'])) != 'NULL'){
// 					return true;
// 				}
// 		}

		if (gettype($this->getValue()) != 'NULL'){
			return true;
		}

		return false;
	}


	/**
	 * @desc Liefert alle entstandenen Fehler.
	 * @return array
	 * */
	public function getError () {
		return $this->error;
	}


	/**
	 * @desc Liefert die Anzahl an internen und externen Fehlern.
	 * @return int
	 * */
	public function hasError () {
		if ($this->error === NULL) {
			return false;
		}

		return true;
	}


	/**
	 * @desc Gibt an, dass Fehlercodes aus der Validierung angezeigt werden sollen
	 * @return void|boolean
	 * */
	public function enableErrorReporting ($_enable = NULL) {
		if (gettype($_enable) == 'boolean') { // Wert setzen
			$this->enableErrorReporting = $_enable;
		} else { // Wert auslesen
			return $this->enableErrorReporting;
		}
	}


	/**
	 * @desc Validiert alle Formulareingaben.
	 * @return int|NULL	: Validierung erfolgreich=true, sonst false
	 * */
	public function validate () {
		if (!count($this->arrParam)) {
			return false;
		}
		if ($this->isRequired() === true && $this->hasSend() === false) {
			return false;
		}
		if ($this->error !== NULL) {
			if (!isset($this->arrParam['class'])) {
				$this->arrParam['class'] = '';
			}
			$this->arrParam['class'] .= ' input-error';
			if (!isset($this->arrParam['title'])) {
				$this->arrParam['title'] = '';
			}
			$this->arrParam['title'] = $this->getError();
			return false;
		}
		$arrValidation = $this->getValidationList();
		if (count($arrValidation)) {
			foreach ($arrValidation as $key => $validation) {
				$value = $this->getValue();
				if ($this->elementType == 'checkbox') {			// === Checkbox ===
					$arr = $value;
					if (count($arr)) {
						$error = 0;
						foreach ($arr as $key => $val) {
							if ($validation->isValid($val)) {
								$this->arrRadioParam[$val]['checked'] = 'checked';
							} else if ($this->required === true) {
								$this->error = Yapary_Exception_Error::E_UNDEFINED;
								$error--;
							}
						}
						if ($error != 0) {
							return false;
						}
						if ($this->required === true && count($arr) != count($this->arrRadioParam)) {
							$this->error = Yapary_Exception_Error::E_UNDEFINED;
							return false;
						}
					} else if ($this->required === true) {
						$this->error = Yapary_Exception_Error::E_UNDEFINED;
						return false;
					}

				} else if ((is_string($value) && strlen($value) != 0) || $this->required === true) {
					if ($validation->isValid($value)) {
						//echo$this->elementType.'('.$value.') ';
						switch ($this->elementType) {
							case 'radio':					// === RADIO ===
								$this->arrRadioParam[$value]['checked'] = 'checked';
								break;
							case 'select':					// === SELECT ===
								if (count($this->arrSelectParam)) {
									foreach($this->arrSelectParam as $key => $arrVal) {
										if (isset($this->arrSelectParam[$key]['arr']['selected'])) {
											unset($this->arrSelectParam[$key]['arr']['selected']);
										}
									}
								}
								$this->arrSelectParam[$value]['arr']['selected'] = 'selected';
								break;
							default:						// === INPUT, RADIO, BUTTON ===
								$this->arrParam['value'] = $validation->typedef($value);
						}

					} else {
						$this->error = $validation->getErrMsg($validation->getLastError());
						// Write error
						if (!isset($this->arrParam['class'])) {
							$this->arrParam['class'] = '';
						}
						$this->arrParam['class'] .= ' input-error';
						if (!isset($this->arrParam['title'])) {
							$this->arrParam['title'] = '';
						}
						$this->arrParam['title'] = $validation->getErrMsg($validation->getLastError());
						//echo '<pre>'.print_r($this->arrParam, true).'</pre>';
						return false;
					}
				}


			}
			$this->isValid = true;
			return true;
		}

		return false;
	}


	/**
	 * @desc Gibt das Formular aus.
	 * @return string
	 * */
	public function getElement() {
		if ($this->required) {
			$this->arrParam['required'] = 'required';
		}

// 		if (isset($this->arrParam['placeholder']) && $this->required) {
// 			switch ($this->elementKind) {
// 				case 'input':
// 				case 'textarea':
// 					$this->arrParam['placeholder'] .= ' (*)';
// 			}
// 		} else if ($this->required) {
// 			switch ($this->elementKind) {
// 				case 'input':
// 				case 'textarea':
// 					$this->arrParam['placeholder'] = ' (*)';
// 			}
// 		}

		$arrValidation = $this->getValidationList();
		if (count($arrValidation)) {
			/* @var $oValidation Yapary_Parameter_ICheck */
			$oValidation = $arrValidation[0];
			if ($oValidation->getPattern() !== NULL) {
				switch ($this->elementKind) {
					case 'input':
					case 'textarea':
						$this->arrParam['pattern'] = $oValidation->getPattern();
				}
			}
		}


		$param = $this->serializeParam($this->arrParam);
		switch ($this->elementKind) {
			case 'input':
				switch ($this->elementType) {
					case 'checkbox':
// 						return str_replace('{{REPLACE_ID}}', '<input '.$param.'/>', $this->subject);
// 						break;
						$arrTmp = $this->arrRadioParam; // Daten in tmp Feld speichern, um an Name "[]" anzuhaengen
						if (isset($arrTmp)) {
							foreach ($arrTmp as $key => $val) {
								//if (isset($val['name']) && count($arrTmp) > 1) {
								if (isset($val['name'])) {
									$val['name'] .='[]';
								}
								if ($this->required) {
									$val['required'] = 'required';
								}
								$this->subject = preg_replace('/{{REPLACE_ID}}/', '<input '.$this->serializeParam($val).' />', $this->subject, 1);
							}

							// hidden Variable "_form_formular_element" zur zuverlaessigen hasSend()-ueberpruefung erzeugen
							$name = $this->arrParam['name'].'_form_formular_element';
							$this->subject .= '<input type="hidden" name="'.$name.'" value="1" />';

							return $this->subject;
						}
						case 'radio':
						if (isset($this->arrRadioParam)) {
							foreach ($this->arrRadioParam as $key => $val) {
								if ($this->required) {
									$val['required'] = 'required';
								}
								$this->subject = preg_replace('/{{REPLACE_ID}}/', '<input '.$this->serializeParam($val).' />', $this->subject, 1);
							}

							// hidden Variable "_form_formular_element" zur zuverflaessigen hasSend()-ueberpruefung erzeugen
//							$name = $this->arrParam['name'].'_form_formular_element';
//							$this->subject .= '<input type="hidden" name="'.$name.'" value="1" />';

							return $this->subject;
						}
					default:
						return str_replace('{{REPLACE_ID}}', '<input '.$param.'/>', $this->subject);
						break;

				}
				break;
			case 'textarea':
				//echo '<pre>'.print_r($this->arrParam, true).'</pre>';
				if (isset($this->arrParam)) {
					if (!isset($this->arrParam['value'])) {
						$value = '';
					} else {
						$value = $this->arrParam['value'];
					}
				} else {
					$value = '';
				}
				if (isset($this->arrParam['textarea'])) {
					unset($this->arrParam['textarea']);
				}
				if (isset($this->arrParam['pattern'])) { // no pattern attribute allowed
					unset($this->arrParam['pattern']);
				}

				$param = $this->serializeParam($this->arrParam);
				return str_replace('{{REPLACE_ID}}', '<textarea '.$param.'>'.$value.'</textarea>', $this->subject);
				break;
			case 'button':
					if (isset($this->arrParam)) {
						if (!isset($this->arrParam['value'])) {
							$value = '';
						} else {
							$value = $this->arrParam['value'];
						}
					} else {
						$value = '';
					}
					return $this->subject;
					break;
			case 'select':
				if (isset($this->arrSelectParam)) {
					if ($this->required) {
						$firstKey = NULL;
						if ($firstKey === NULL) {
							$firstKey = key($this->arrSelectParam);
						}
						foreach ($this->arrSelectParam as $key => $arrVal) {
							if (isset($arrVal['arr']['selected'])) {
								$firstKey = $key;
							}
						}
						$this->arrSelectParam[$firstKey]['name'] .= ' (*)';
					}

					$value = $this->serializeSelectParam($this->arrSelectParam);
				} else {
					$value = '';
				}
				return str_replace('{{REPLACE_ID}}', '<select '.$param.'>'.$value.'</select>', $this->subject);
				break;
		}
	}


}


?>